/***************************************************************************
 *   oboe.utils.MD2Parser                                                  *
 *   Copyright (C) 2011 by Felipe Manga                                    *
 *   lufeboma@gmail.com                                                    *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

package oboe.utils
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.ByteArray;
	
	import oboe.core.Oboe;
	import oboe.math.Mesh3D;
	import oboe.math.Number2D;
	import oboe.math.Number3D;
	import oboe.math.Vertex3D;
	
	public class MD2Parser extends EventDispatcher
	{
		public var data:Mesh3D;
		private var rawData:ByteArray;
		private var state:Function;
		private var head:Header;
		
		public function MD2Parser( data:ByteArray )
		{
			super();
			this.rawData = data;
			Oboe.stage.addEventListener(Event.ENTER_FRAME, onStep);
			this.state = parseHeader;
		}
		
		private var stepId:int=0;
		private function onStep( e:Event ):void
		{
			for( var i:int=0; i<20; ++i )
			{
				this.state();
			}
			// trace("step id:", ++stepId);
		}
		
		private function parseHeader():void
		{
			this.head = new Header( rawData );
			this.state = readBody;
		}
		
		private function readBody():void
		{
			readUVs();
			readIndices();
			readFrames();
			this.state = setup1;
		}
		
		private var uvlist:Vector.<Number>;
		private var frames:Vector.< Vector.<Number3D> >;
		private var lf:int;
		private var frame:Vector.<Number3D>;
		private var vframe:Vector.<Vertex3D>;
		
		private function setup1():void
		{
			this.uvlist = new Vector.<Number>( head.frames[0].length*2 );
			this.frames = new Vector.<Vector.<Number3D>>( head.frames.length, true );
			this.lf = frames.length;
			this.vframe = head.frames[0];
			for( var f:int=0; f<lf; ++f )
			{
				frames[f] = new Vector.<Number3D>( head.frames[f].length );
			}
			
			frame = frames[0];
			s2it = 0;
			s2l = head.indices.length;
			s2fl =frame.length-1;
			this.state = setup2;
		}
		
		private var s2it:int;
		private var s2l:int;
		private var s2fl:int;
		
		private function setup2():void
		{
			var f:int;
			if( s2it < s2l )
			{
				var uv:Number2D = head.uv[ s2it ];
				var vid:int = head.indices[s2it];
				var vert:Vertex3D = vframe[ vid ];
				if( !vert.uvl )
				{
					vert.uvl = new Hash();
					vert.uvl[ uv ] = s2it;
					uvlist[ vid*2 ] = uv.x;
					uvlist[ vid*2+1 ] = uv.y;
					for( f=0; f<lf; ++f )
					{
						frames[f][ vid ] = head.frames[f][ vid ];
					}
				}
				else if( uv in vert.uvl )
				{
					vid = head.indices[ vert.uvl[uv] ];
					for( f=0; f<lf; ++f )
					{
						frames[f][ vid ] = head.frames[f][ vid ];
					}
				}
				else
				{
					vert.uvl[ uv ] = s2it;
					head.indices[s2it] = s2fl;
					uvlist[ s2fl*2 ] = uv.x;
					uvlist[ s2fl*2+1 ] = uv.y;
					for( f=0; f<lf; ++f )
					{
						var fvert:Vertex3D = head.frames[f][ vid ];
						frames[f][s2fl] = new Number3D( fvert.x, fvert.y, fvert.z );
					}
					s2fl++;
				}
				
				++s2it;
			}
			else
			{
				this.state = done;
				this.data = new Mesh3D( head.indices, frames, uvlist );
				this.dispatchEvent( new Event( Event.COMPLETE ) );
			}
		}
		
		private function done():void
		{
			Oboe.stage.removeEventListener(Event.ENTER_FRAME, onStep);
		}
			
		private function readUVs() : void {
			// s.seekg( head.offset_st, ios::beg );
			rawData.position = head.offset_st;
			for( var uvid:int=0; uvid<head.num_st; ++uvid ){
				//Twin< short > uv(0,0);
				head.uvlist[ uvid*2 ] = rawData.readShort() / head.skinwidth;
				//s.read( (char *)&uv.x, sizeof(short) );
				head.uvlist[ uvid*2+1] = rawData.readShort() / head.skinheight;
				//s.read( (char *)&uv.y, sizeof(short) );
				//r->uvs.push_back( Twin<float>( float( uv.x ) / float(head.skinwidth), float(uv.y) / float(head.skinheight) ) );
			}
		}
		
		private function readIndices() : void {
			// s.seekg( head.offset_tris, ios::beg );
			rawData.position = head.offset_tris;
			for( var tid:int=0; tid<head.num_tris; ++tid ){
				// Triplet< unsigned short > t;
				head.indices[ tid*3+0 ] = (rawData.readShort() & 0xFFFF);
				head.indices[ tid*3+1 ] = (rawData.readShort() & 0xFFFF);
				head.indices[ tid*3+2 ] = (rawData.readShort() & 0xFFFF);
				// s.read( (char *)&t, sizeof(t) );
				// r->indices.push_back( t );
				
				// read UV indices
				var uvi:int = (rawData.readShort() & 0xFFFF) * 2;
				head.uv[ tid*3+0 ] = new Number2D( head.uvlist[ uvi ], head.uvlist[ uvi+1 ] );
				
				uvi = (rawData.readShort() & 0xFFFF) * 2;
				head.uv[ tid*3+1 ] = new Number2D( head.uvlist[ uvi ], head.uvlist[ uvi+1 ] );
				
				uvi = (rawData.readShort() & 0xFFFF) * 2;
				head.uv[ tid*3+2 ] = new Number2D( head.uvlist[ uvi ], head.uvlist[ uvi+1 ] );
				/*
				head.indices[ tid*6+3 ] = (rawData.readShort() & 0xFFFF) * 2;
				head.indices[ tid*6+4 ] = (rawData.readShort() & 0xFFFF) * 2;
				head.indices[ tid*6+5 ] = (rawData.readShort() & 0xFFFF) * 2;
				*/
			}
		}
		
		private function readFrames() : void {
			rawData.position = head.offset_frames;
			// s.seekg( head.offset_frames, ios::beg );
			var ws : Number = Oboe.flags["SCALE_3D"];
			for( var fid:int=0; fid<head.num_frames; ++fid ){
				var scale : Number3D = new Number3D(0,0,0), 
					trans : Number3D = new Number3D(0,0,0);
				var name : String;
				var f:Vector.<Vertex3D> = head.frames[fid];
				//r->frames.push_back( f );
				
				scale.readFloat( rawData ).mul(ws);
				// s.read( (char *) &scale, sizeof(scale) );
				trans.readFloat( rawData ).mul(ws);
				// s.read( (char *) &trans, sizeof(trans) );
				rawData.position += 16; // skip name
				// s.read( name, 16 );
				// name[15] = 0;
				// cout << "reading frame " << name << " @" << s.tellg() << endl;
				for( var i:int=0; i<head.num_vertices; ++i ){
					var v : Vertex3D = new Vertex3D(0,0,0);
					// Triplet<unsigned char> d;
					// Vertex3D v;
					v.readByte( rawData );
					// s.read( (char *)&d, sizeof(d) );
					
					// v = d;
					v.mul3( scale ).add3( trans ).flipYZ();
					// v *= scale;
					// v += trans;
					// swap( v.z, v.y );
					
					f[ i ] = v;
					// f->push_back( v );
					
					rawData.readByte();
					// s.read( (char *)&d, 1 ); // read normal index
				}
			}
		}
	}
}
import flash.utils.ByteArray;
import flash.utils.Endian;

import oboe.math.Number2D;
import oboe.math.Vertex3D;

internal class Header {
	public var  ident : int;                  /* magic number: "IDP2" */
	public var  version : int;                /* version: must be 8 */
	
	public var  skinwidth : int;              /* texture width */
	public var  skinheight : int;             /* texture height */
	
	public var  framesize : int;              /* size in bytes of a frame */
	
	public var  num_skins : int;              /* number of skins */
	public var  num_vertices : int;           /* number of vertices per frame */
	public var  num_st : int;                 /* number of texture coordinates */
	public var  num_tris : int;               /* number of triangles */
	public var  num_glcmds : int;             /* number of opengl commands */
	public var  num_frames : int;             /* number of frames */
	
	public var  offset_skins : int;           /* offset skin rawData */
	public var  offset_st : int;              /* offset texture coordinate rawData */
	public var  offset_tris : int;            /* offset triangle rawData */
	public var  offset_frames : int;          /* offset frame rawData */
	public var  offset_glcmds : int;          /* offset OpenGL command rawData */
	public var  offset_end : int;             /* offset end of file */
	
	public var indices:Vector.<int>;
	public var uvlist : Vector.< Number >;
	public var uv : Vector.<Number2D>;
	public var frames : Vector.< Vector.<Vertex3D> >;
	
	public function Header( rawData : ByteArray ) {
		rawData.position = 0;
		rawData.endian = Endian.LITTLE_ENDIAN;
		
		ident = rawData.readInt();					 /* magic number: "IDP2" */
		version = rawData.readInt();				 /* version: must be 8 */
		skinwidth = rawData.readInt();
		skinheight = rawData.readInt();
		framesize = rawData.readInt();              /* size in bytes of a frame */
		
		num_skins = rawData.readInt();              /* number of skins */
		num_vertices = rawData.readInt();           /* number of vertices per frame */
		num_st = rawData.readInt();                 /* number of texture coordinates */
		num_tris = rawData.readInt();               /* number of triangles */
		num_glcmds = rawData.readInt();             /* number of opengl commands */
		num_frames = rawData.readInt();             /* number of frames */
		
		offset_skins = rawData.readInt();           /* offset skin rawData */
		offset_st = rawData.readInt();              /* offset texture coordinate rawData */
		offset_tris = rawData.readInt();            /* offset triangle rawData */
		offset_frames = rawData.readInt();          /* offset frame rawData */
		offset_glcmds = rawData.readInt();          /* offset OpenGL command rawData */
		offset_end = rawData.readInt();             /* offset end of file */
		
		indices = new Vector.<int>( num_tris*3, true );
		uv = new Vector.<Number2D>( num_tris*3, true );
		frames = new Vector.< Vector.<Vertex3D> >( num_frames, true );
		uvlist = new Vector.<Number>( num_st*2, true );
		for( var i:int=0; i<num_frames; ++i )
		{
			frames[i] = new Vector.<Vertex3D>( num_vertices, true );
		}
	}
}
